<?php

namespace app\cms\community\lib\Query;

use iflow\swoole\implement\Services\Elasticsearch\Elasticsearch as Els;

trait Elasticsearch {

    protected Els $elasticsearch;

    protected string $elsTypeName = '_doc';

    // 根据模型 进行 elasticsearch 查询 只支持单表 且 elasticsearch 模型名称须以表单名称一致
    protected string $modelName = '';
    protected string $indexName = '';

    /**
     * 字段映射
     * @var array|array[]
     */
    protected array $mappingElsField = [
        'properties' => []
    ];

    /**
     * 检查 ElasticSearch indices 索引 是否存在
     * @return $this
     */
    protected function checkIndicesExitsAndCreated(): static {
        $this->elasticsearch = new Els('queryTables');

        $this->setIndexType();

        // 不存在则创建
        if (!$this->elasticsearch -> indices() -> indicesExists($this->indexName)) {
            $this->elasticsearch -> indices() -> createIndices($this->indexName);
            $this->elasticsearch -> mappings()
                -> setMappingsOptions($this->mappingElsField)
                -> setMappings(
                    $this->indexName,
                    $this->elsTypeName
                );
        }

        return $this;
    }

    /**
     * 生成映射字段数据
     * @param array $docs
     * @return array
     */
    protected function builderMappingDocs(array $docs): array {
        if (empty($docs)) return [];

        return array_map(function ($value) {
            $save = [];
            foreach ($this->mappingElsField['properties'] as $field) {
                $save[$field] = $value[$field];
            }
            $save['_id'] = $save['_id'] ?? $save[$this -> pk];
            return $save;
        }, $docs);
    }

    /**
     * 设置索引
     * @return void
     */
    protected function setIndexType() {
        $this->indexName = $this->indexName ?: $this -> table;
    }

    /**
     * 将数据写入Elasticsearch
     * @param array $docs
     * @param bool $isUpdate
     * @return mixed
     */
    public function saveToElasticSearch(array $docs = [], bool $isUpdate = false): mixed {

        $this->setIndexType();
        if (!$isUpdate && !$this->elasticsearch -> indices() -> indicesExists($this -> table)) {
            return false;
        }

        $this->checkIndicesExitsAndCreated();
        $docs = $docs ?: $this -> toArray();

        $action = $isUpdate ? 'updateDocsBulk' : 'createDoc';
        return call_user_func([ $this->elasticsearch, $action ], $this->builderMappingDocs($docs), $this->table, $this->elsTypeName);
    }

    /**
     * 删除字段
     * @param array $docs
     * @return bool
     */
    public function deleteToElasticSearch(array $docs = []): bool {
        $this->setIndexType();
        if ($this->elasticsearch -> indices() -> indicesExists($this->indexName)) {
            $docs = $docs ?: $this -> toArray();
            if (empty($docs)) return false;

            return $this->elasticsearch -> docs() -> deleteDocsBulk(
                $this->builderMappingDocs($docs), $this -> indexName, $this->elsTypeName
            );
        }
        return false;
    }

    /**
     * @param string $indexName
     * @return static
     */
    public function setIndexName(string $indexName): static {
        $this->indexName = $indexName;
        return $this;
    }

}